package com.atguigu.gmall.payment.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.internal.util.AlipaySignature;
import com.alipay.api.request.*;
import com.alipay.api.response.*;
import com.atguigu.gmall.enums.model.PaymentStatus;
import com.atguigu.gmall.enums.model.PaymentType;
import com.atguigu.gmall.order.client.OrderFeignClient;
import com.atguigu.gmall.order.model.OrderInfo;
import com.atguigu.gmall.payment.config.AlipayConfig;
import com.atguigu.gmall.payment.model.PaymentInfo;
import com.atguigu.gmall.payment.service.AlipayService;
import com.atguigu.gmall.payment.service.PaymentInfoService;
import com.atguigu.gmall.rabbit.config.MqConst;
import com.atguigu.gmall.rabbit.service.RabbitService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.aspectj.weaver.ast.Var;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.Arrays;
import java.util.Date;
import java.util.Map;

/**
 * @author: atguigu
 * @create: 2023-08-12 15:27
 */
@Slf4j
@Service
public class AlipayServiceImpl implements AlipayService {

    @Autowired
    private PaymentInfoService paymentInfoService;

    @Autowired
    private OrderFeignClient orderFeignClient;

    @Autowired
    private AlipayClient alipayClient;

    @Autowired
    private RabbitService rabbitService;

    /**
     * 调用支付宝接口生成支付表单
     *
     * @param userId
     * @param orderId
     * @return
     */
    @Override
    public String createAlipayForm(String userId, Long orderId) {
        try {
            //1.先保存本地交易记录
            //1.1 创建本地交易记录为本地交易记录属性赋值
            PaymentInfo paymentInfo = new PaymentInfo();
            //1.2 调用订单服务获取订单信息
            OrderInfo orderInfo = orderFeignClient.getOrderInfo(orderId);
            if (orderInfo != null) {
                //1.3 为本地交易记录中属性赋值
                paymentInfo.setOutTradeNo(orderInfo.getOutTradeNo());
                paymentInfo.setOrderId(orderId);
                paymentInfo.setUserId(orderInfo.getUserId());
                paymentInfo.setPaymentType(PaymentType.ALIPAY.name());
                //paymentInfo.setTotalAmount(orderInfo.getTotalAmount());
                //TODO 开发阶段设置为0.01 一分钱
                paymentInfo.setTotalAmount(new BigDecimal("0.01"));
                paymentInfo.setSubject(orderInfo.getTradeBody());
                paymentInfo.setPaymentStatus(PaymentStatus.UNPAID.name());
                paymentInfoService.savePaymentInfo(paymentInfo, PaymentType.ALIPAY.name());

                //2.调用支付宝接口（显示支付页面接口）
                //2.1 创建支付页面请求对象
                //AlipayTradePagePayRequest request = new AlipayTradePagePayRequest();
                AlipayTradeWapPayRequest request = new AlipayTradeWapPayRequest();
                ///2.1.1 设置同步回调地址-给用户看支付成功页面
                request.setReturnUrl(AlipayConfig.return_payment_url);
                ///2.1.2 设置异步回调地址-支付宝给系统通知
                request.setNotifyUrl(AlipayConfig.notify_payment_url);

                //2.2 设置请求头参数：必传参数
                JSONObject bizContent = new JSONObject();
                //商户订单号，商家自定义，保持唯一性
                bizContent.put("out_trade_no", orderInfo.getOutTradeNo());
                //支付金额，最小值0.01元
                bizContent.put("total_amount", paymentInfo.getTotalAmount());
                //订单标题，不可使用特殊符号
                bizContent.put("subject", orderInfo.getTradeBody());
                //电脑网站支付场景固定传值FAST_INSTANT_TRADE_PAY
                //bizContent.put("product_code", "FAST_INSTANT_TRADE_PAY");
                bizContent.put("product_code", "QUICK_WAP_WAY");
                //支付二维码过期时间
                bizContent.put("timeout_express", "10m");

                //2.3 调用支付宝接口
                request.setBizContent(bizContent.toString());
                //AlipayTradePagePayResponse response = alipayClient.pageExecute(request);
                AlipayTradeWapPayResponse response = alipayClient.pageExecute(request);
                //2.4 从响应对象获取支付表单页面
                if (response.isSuccess()) {
                    return response.getBody();
                }
            }
        } catch (Exception e) {
            throw new RuntimeException("展示支付页面异常");
        }
        return null;
    }

    /**
     * 处理支付宝异步回调（支付宝通知支付系统告知用户支付结果）
     *
     * @param mapParam
     * @return
     */
    @Override
    public String notifyPaySuccess(Map mapParam) {
        try {
            log.info("支付宝提交异步通知参数：{}", mapParam);
            //1.对提交参数进行”验签“，对提交所有参数进行验证签名，避免出现提交参数篡改（避免出现”假“通知）”
            boolean signVerified = AlipaySignature.rsaCheckV1(mapParam, AlipayConfig.alipay_public_key, AlipayConfig.charset, AlipayConfig.sign_type); //调用SDK验证签名
            if (!signVerified) {
                return "failure";
            }
            //2.验证订单是否为商家所有
            //2.1 得到支付宝提交商户订单编号
            String outTradeNo = (String) mapParam.get("out_trade_no");
            //2.2 根据订单编号+支付方式 查询本地交易记录
            PaymentInfo paymentInfo = paymentInfoService.getPaymentInfo(outTradeNo, PaymentType.ALIPAY.name());
            if (paymentInfo == null) {
                return "failure";
            }
            //3.验证支付宝交易金额跟商家端订单金额是否一致
            //3.1 得到支付宝提交发生交易金额数
            String totalAmount = (String) mapParam.get("total_amount");
            //3.2 判断金额是否一致
            if (paymentInfo.getTotalAmount().compareTo(new BigDecimal(totalAmount)) != 0) {
                return "failure";
            }

            //4.验证appId是否为商户所有
            String appId = (String) mapParam.get("app_id");
            if (!appId.equals(AlipayConfig.app_id)) {
                return "failure";
            }

            //5.验证支付宝端交易状态 如果是“TRADE_SUCCESS”（用户付钱成功）
            String tradeStatus = (String) mapParam.get("trade_status");
            if ("TRADE_SUCCESS".equals(tradeStatus)) {
                //6.修改本地交易记录 包括：支付宝交易号跟本地交易记录关联；回调信息；支付状态：已支付
                paymentInfo.setPaymentStatus(PaymentStatus.PAID.name());
                paymentInfo.setCallbackTime(new Date());
                paymentInfo.setCallbackContent(mapParam.toString());
                String tradeNo = (String) mapParam.get("trade_no");
                //只有用户付款成功后，才会将本地交易记录关联到支付宝端交易号
                paymentInfo.setTradeNo(tradeNo);
                paymentInfoService.updateById(paymentInfo);


                //7.TODO 订单服务需改订单状态改为“已支付”
                rabbitService.sendMsg(MqConst.EXCHANGE_DIRECT_PAYMENT_PAY, MqConst.ROUTING_PAYMENT_PAY, paymentInfo.getOrderId());
                return "success";
            }

        } catch (Exception e) {
            log.error("[支付服务]处理支付宝异步回调异常：{}", e);
            throw new RuntimeException(e);
        }
        return "failure";
    }

    /**
     * 进行支付宝退款业务处理
     *
     * @param outTradeNo
     */
    @Override
    public void refund(String outTradeNo) {
        try {
            //1.验证本地交易记录状态=“已支付”，调用支付宝接口完成退款
            PaymentInfo paymentInfo = paymentInfoService.getPaymentInfo(outTradeNo, PaymentType.ALIPAY.name());
            if (paymentInfo != null && PaymentStatus.PAID.name().equals(paymentInfo.getPaymentStatus())) {
                //调用支付宝进行退款
                AlipayTradeRefundRequest request = new AlipayTradeRefundRequest();
                JSONObject bizContent = new JSONObject();
                if (StringUtils.isNotBlank(paymentInfo.getTradeNo())) {
                    bizContent.put("trade_no", paymentInfo.getTradeNo());
                }
                if (StringUtils.isNotBlank(paymentInfo.getOutTradeNo())) {
                    bizContent.put("out_trade_no", paymentInfo.getOutTradeNo());
                }
                bizContent.put("refund_amount", paymentInfo.getTotalAmount());
                bizContent.put("out_request_no", "HZ01RF001");
                request.setBizContent(bizContent.toString());
                AlipayTradeRefundResponse response = alipayClient.execute(request);
                if (response.isSuccess()) {
                    if ("Y".equals(response.getFundChange())) {
                        //2.修改本地交易记录状态
                        paymentInfo.setPaymentStatus(PaymentStatus.CLOSED.name());
                        paymentInfoService.updateById(paymentInfo);

                        //3.TODO 远程调用订单服务 修改订单交易记录状态
                    }
                }
            }
        } catch (Exception e) {
            log.error("[支付服务]订单：{}，退款异常：{}", outTradeNo, e);
            throw new RuntimeException(e);
        }
    }

    /**
     * 查询支付宝交易状态
     *
     * @param paymentInfo
     * @return
     */
    @Override
    public String getAliPayTradeSatus(PaymentInfo paymentInfo) {
        try {
            AlipayTradeQueryRequest request = new AlipayTradeQueryRequest();
            JSONObject jsonObject = new JSONObject();
            if (StringUtils.isNotBlank(paymentInfo.getOutTradeNo())) {
                jsonObject.put("out_trade_no", paymentInfo.getOutTradeNo());
            }
            if (StringUtils.isNotBlank(paymentInfo.getTradeNo())) {
                //todo 支付宝交易号，只有付款成功后才有值
                jsonObject.put("trade_no", null);
            }
            //jsonObject.put("query_options", Arrays.asList("trade_settle_info"));
            request.setBizContent(jsonObject.toJSONString());
            AlipayTradeQueryResponse response = alipayClient.execute(request);
            if (response.isSuccess()) {
                return response.getTradeStatus();
            }
        } catch (Exception e) {
            log.error("[支付宝]获取交易状态异常：{}", e);
            throw new RuntimeException(e);
        }
        return null;
    }

    /**
     * 关闭支付宝交易
     *
     * @param paymentInfo
     */
    @Override
    public void closeAlipayTrade(PaymentInfo paymentInfo) {
        try {
            AlipayTradeCloseRequest request = new AlipayTradeCloseRequest();
            JSONObject bizContent = new JSONObject();
            if (StringUtils.isNotBlank(paymentInfo.getOutTradeNo())) {
                bizContent.put("out_trade_no", paymentInfo.getOutTradeNo());
            }
            if (StringUtils.isNotBlank(paymentInfo.getTradeNo())) {
                //todo 支付宝交易号，只有付款成功后才有值
                bizContent.put("trade_no", paymentInfo.getTradeNo());
            }
            request.setBizContent(bizContent.toString());
            AlipayTradeCloseResponse response = alipayClient.execute(request);
            if (response.isSuccess()) {
                System.out.println("调用成功");
            } else {
                System.out.println("调用失败");
            }
        } catch (Exception e) {
            log.error("[支付宝]关闭交易异常：{}", e);
            throw new RuntimeException(e);
        }
    }
}
